package edu.mtu.citizenscience.cs.sdk.uploader.model;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import android.content.Context;

import edu.mtu.citizenscience.cs.sdk.uploader.model.entry.FileEntry;
import edu.mtu.citizenscience.cs.sdk.uploader.model.entry.LocationEntry;
/**
 * @author Corbin Uselton <corbinu@cwuselto@mtu.edu>
 * @copyright (c) 2012 MichiganTech <http://www.mtu.edu>
 * @since 1.0
 * 
 * A row in the apps database
 */
public class Row {
	// the name of the table the row belongs to
	private String table;
	// the row id in the DB
	private long id;
	// entries in the row
	private List<Entry<?>> entries = new ArrayList<Entry<?>>();
	// child rows in other tables if any
	private List<Row> children = new ArrayList<Row>();
	// file stored in the row if any
	private FileEntry file;
	// location stored in the row if any
	private LocationEntry location;
	
	/**
	 * Get the row table name
	 * 
	 * @param   void
	 * @return	String - table name
	 */
	public String getTable()
	{
		return this.table;
	}
	
	/**
	 * Set the row table name
	 * 
	 * @param   table - String - table name
	 * @return	void
	 */
	public void setTable(String table)
	{
		this.table = table;
	}
	
	/**
	 * Get the row id
	 * 
	 * @param   void
	 * @return	long - row id
	 */
	public long getId()
	{
		return this.id;
	}
	
	/**
	 * Set the row id
	 * 
	 * @param   id - long - row id
	 * @return	void
	 */
	public void setId(long id)
	{
		this.id = id;
	}
	
	/**
	 * Get the row entries
	 * 
	 * @param   void
	 * @return	List<Entry<?>> - list of row entries
	 */
	public List<Entry<?>> getEntries()
	{
		return this.entries;
	}
	
	/**
	 * Set the row entries
	 * 
	 * @param   entries - List<Entry<?>> - list of row entries
	 * @return	void
	 */
	public void setEntries(List<Entry<?>> entries)
	{
		this.entries = entries;
	}
	
	/**
	 * Check if the row has children
	 * 
	 * @param   void
	 * @return	boolean - whether the row has children or not
	 */
	public boolean haveChildren()
	{
		return (this.children.size() > 0);
	}
	
	/**
	 * Get row children
	 * 
	 * @param   void
	 * @return	List<Row> - list of child rows
	 */
	public List<Row> getChildren()
	{
		return this.children;
	}
	
	/**
	 * Set row children
	 * 
	 * @param   children - List<Row> - list of child rows
	 * @return	void
	 */
	public void setChildren(List<Row> children)
	{
		this.children = children;
	}
	
	/**
	 * Get row location entry
	 * 
	 * @param   void
	 * @return	LocationEntry - the row location entry
	 */
	public LocationEntry getLocationEntry()
	{
		return this.location;
	}
	
	/**
	 * Set row location entry
	 * 
	 * @param   location - LocationEntry - the row location entry
	 * @return	void
	 */
	public void setLocationEntry(LocationEntry location)
	{
		this.location = location;
	}
	
	/**
	 * Check if row has a file entry
	 * 
	 * @param   void
	 * @return	boolean
	 */
	public boolean haveFileEntry()
	{
		return (this.file != null);
	}
	
	/**
	 * Get row file entry
	 * 
	 * @param   void
	 * @return	FileEntry - row file entry
	 */
	public FileEntry getFileEntry()
	{
		return this.file;
	}
	
	/**
	 * Set row file entry
	 * 
	 * @param   file - FileEntry - row file entry
	 * @return	void
	 */
	public void setFileEntry(FileEntry file)
	{
		this.file = file;
	}
	
	/**
	 * GeoLocate the location of this and any child rows
	 * 
	 * @param   context - Context - the context to use in geolocation
	 * @return	void
	 */
	public void geoLocateLocations(Context context) throws IOException
	{
		// loop over the rows children
		for (Row child: children)
		{
			// geolocate the child and any of its children
			child.geoLocateLocations(context);
		}
		
		// check if this row has a location
		if (this.location != null)
		{
			// geoloate the location
			this.location.geoLocate(context);
		}
	}
	
	/**
	 * Get the row as a map to upload to the server
	 * 
	 * @param   void
	 * @return	Map<String, Object> - row as a map
	 */
	public Map<String, Object> getAsMap()
	{
		// create a new map to store the row in
		Map<String, Object> map = new HashMap<String, Object>();
		
		// create a new map to store the row entries
		Map<String, Object> entries = new HashMap<String, Object>();
		// loop over the rows entries
		for (Entry<?> entry : this.entries)
		{
			// put the entry name and value into the map formating the name
			entries.put(this.convertName(entry.getName()), entry.getValue());
		}
		
		// check if there are entries
		if (entries.size() > 0)
		{
			// put the entries in the map
			map.put("entries", entries);
		}
		
		// create a list for holding child row maps
		List<Map<String, Object>> children = new ArrayList<Map<String, Object>>();
		// loop over the rows children
		for (Row row : this.children)
		{
			// get the child row as a map as well and add it to the children list
			children.add(row.getAsMap());
		}
		
		// check if there are children
		if (children.size() > 0)
		{
			// put the children in the map
			map.put("children", children);
		}
		
		// check if the row has a file
		if (this.file != null)
		{
			// put the file in the map
			map.put("file", this.file.getAsMap());
		}
		
		// check if the row has a location
		if (this.location != null)
		{
			// put the location in the map
			map.put("location", this.location.getMap());
		}
		
		// return the row map
		return map;
	}
	
	/**
	 * Convert the row name from underscore to camelcase
	 * 
	 * @param   name - String - underscore case name
	 * @return	String - camelcase case name
	 */
	private String convertName(String name)
	{
		// check if the name starts with an underscore
		if (name.indexOf('_') == 0)
		{
			// remove the underscore
			name = name.substring(1);
		}
		// convert name to lower case
		name = name.toLowerCase(Locale.US);
		
		// split the name parts by the underscore
		String nameArray[] = name.split("_");
		
		// create a new string to store the camelcase name
		String camelName = "";
		
		// mark as true for first part
		boolean first = true;
		// loop over the name parts
		for (String namePart : nameArray)
		{
			// check if this the first part
			if (first)
			{
				// add the first name part to the camel name
				camelName += namePart;
				// set first as false
				first = false;
			}
			else
			{
				// capitalize the first letter and add the name part to the camel name
				camelName += namePart.substring(0, 1).toUpperCase(Locale.US) + namePart.substring(1);
			}
		}
		
		// return the camel name
		return camelName;
	}
	
}